package com.gitee.ywj1352.gateway.servlet;

import com.gitee.ywj1352.gateway.Iloadbalance.ZookeeperLoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.Set;

/**
 * @author wenjie.yang on 2019/8/1.
 * /gateway/{service-name}/{service-uri} -> http://127.0.0.1:9091/hello-world
 */
@WebServlet(name = "gateway",urlPatterns = "/gateway/*")
public class GatewayServlet extends HttpServlet {


    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private ZookeeperLoadBalancer zookeeperLoadBalancer;

    /**
     * 原始的选择器
     * @param serviceName
     * @return
     */
    private ServiceInstance randomChooseServicInstance(String serviceName){
        List<ServiceInstance> serviceList = discoveryClient.getInstances(serviceName);
        Random random = new Random();
        int i = random.nextInt(serviceList.size());
        return serviceList.get(i);
    }


    //proxy -> POST GET
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        RestTemplate restTemplate = new RestTemplate();
        //abc -> app name ； a 5 IP Load Balance
        String pathInfo = req.getPathInfo();
        String[] parts = StringUtils.split(pathInfo.substring(1), "/");
        //获取服务名
        String serviceName = parts[0];
        //获取服务uri
        String serviceURI = "/" + parts[1];
        //获取服务实例列表(服务主机Ip port isHttps)
        Server serviceInstance = chooseServer(serviceName);

        String url = serviceInstance.getScheme()+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/"+serviceURI;
        RequestEntity<byte[]> requestEntity = null;
        try {
            requestEntity =  createRequestEntity(req,url);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        ResponseEntity<byte[]> responseEntity = restTemplate.exchange(requestEntity, byte[].class);
        writeHeader(responseEntity,resp);
        writeBody(responseEntity,resp);
    }
    //把响应体中的header 传给  response
    private void writeHeader( ResponseEntity<byte[]> responseEntity ,HttpServletResponse response){
        HttpHeaders headers = responseEntity.getHeaders();
        Set<String> headerNames = headers.keySet();
        for (String headerName:headerNames){

            for(String headerValue:headers.get(headerName)){
                response.addHeader(headerName,headerValue);
            }
        }
    }

    /**
     * 采用 Robin 的选择器
     * @param serviceName
     * @return
     */
    private Server chooseServer(String serviceName){
       return  zookeeperLoadBalancer.chooseServer(serviceName);
    }

    private void writeBody( ResponseEntity<byte[]> responseEntity ,HttpServletResponse response) throws IOException {
        byte[] body = responseEntity.getBody();
        ServletOutputStream outputStream = response.getOutputStream();
        //writer 是写 字符  ops 是写 流更加原生
        outputStream.write(body);
        //要刷新才出去
        outputStream.flush();
    }




    private MultiValueMap<String,String> createHeader(HttpServletRequest req) {
        HttpHeaders httpHeaders = new HttpHeaders();
        List<String> headerNames = Collections.list(req.getHeaderNames());
        for (String headerName:headerNames){
            List<String> headerValues = Collections.list(req.getHeaders(headerName));
            for (String value:headerValues){
                httpHeaders.add(headerName,value);
            }
        }
        return httpHeaders;
    }


    private RequestEntity<byte[]> createRequestEntity(HttpServletRequest request,String url) throws IOException, URISyntaxException {
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(method);
        MultiValueMap<String, String> header = createHeader(request);
        byte[] bytes = coypeBody(request);
        URI uri = new URI(url);
        return new RequestEntity<byte[]>(bytes,header,httpMethod,uri);
    }

    /**
     * copy 输入流 给 下一次请求
     * @param request
     * @return
     * @throws IOException
     */
    private byte[] coypeBody(HttpServletRequest request) throws IOException {
        InputStream inputStream = request.getInputStream();
        return StreamUtils.copyToByteArray(inputStream);
    }
}
